/* Copyright (c) 2004-2024 Peigen.info. All rights reserved. */

package info.peigen.hotpot.component.open.api.core.service.processor.base.pre;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import info.peigen.hotpot.business.customer.facade.info.partner.PartnerInfo;
import info.peigen.hotpot.business.customer.facade.info.partner.PartnerTechInfo;
import info.peigen.hotpot.common.service.base.exception.BusinessException;
import info.peigen.hotpot.component.open.api.core.service.processor.base.AbstractOpenApiServiceProcessor;
import info.peigen.hotpot.component.open.api.facade.order.OpenApiOrder;
import info.peigen.hotpot.component.open.api.facade.processor.base.OpenApiProcessor;
import info.peigen.hotpot.component.open.api.facade.processor.base.OpenApiProcessorServiceContext;
import org.noear.solon.annotation.Component;

import java.util.Map;
import java.util.TreeMap;

/**
 * <b>(OpenApiSignatureProcessor)</b>
 * 参数签名验证
 *
 * @author LiDaHai
 * @version 1.0.0
 * @since 2022/9/14
 */
@SuppressWarnings({"rawtypes"})
@Component
public class OpenApiSignatureProcessor extends AbstractOpenApiServiceProcessor implements OpenApiProcessor {

    @Override
    public Integer level() {
        return 6;
    }

    @Override
    public void execute(OpenApiProcessorServiceContext serviceContext) {
        OpenApiOrder order       = serviceContext.getOrder();
        PartnerInfo  partnerInfo = (PartnerInfo) serviceContext.getPartnerInfo();
        String       requestSign = order.getSign();

        // 公共参数
        Map<String, Object> signMap = BeanUtil.beanToMap(order, "requestNo", "apiCode", "partnerId", "userIp", "version");

        // 业务参数
        Map<String, Object> infoMap = new TreeMap<>();
        BeanUtil.copyProperties(JSON.parse(serviceContext.getApi()
                .getDecrypt()), infoMap, "gid", "extend", "partnerId", "bizOrderNo", "productCode");
        infoMap.remove("gid");
        infoMap.remove("extend");
        signMap.put("info", JSON.toJSONString(infoMap, JSONWriter.Feature.WriteMapNullValue, JSONWriter.Feature.WriteNullListAsEmpty));

        String md5Sign = sign(partnerInfo.getTech(), signMap);
        Assert.isTrue(StrUtil.equals(requestSign, md5Sign), () -> new BusinessException("验签失败"));
    }

    private String sign(PartnerTechInfo tech, Map<String, Object> signMap) {
        return switch (tech.getSignType()) {
            case MD5 -> md5(signMap);
            case SM4 -> sm4(signMap, tech);
            default -> null;
        };
    }

    private String md5(Map<String, Object> signMap) {
        return SecureUtil.md5(JSON.toJSONString(MapUtil.sort(signMap)));
    }

    private String sm4(Map<String, Object> signMap, PartnerTechInfo tech) {
        SymmetricCrypto crypto = SmUtil.sm4(HexUtil.decodeHex(tech.getSignKey()))
                .setIv(tech.getIv());

        return crypto.encryptHex(JSON.toJSONString(signMap));
    }
}